<?php
/**
 * Copyright (c) 2006-2007, Julien PORTALIER
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright    Copyright (c) 2006-2007, Julien PORTALIER
 * @link         http://featherphp.googlecode.com/
 * @license      http://www.opensource.org/licenses/mit-license.php The MIT License
 */

/**
 * Dispatches the given URL to correct controller/action.
 *
 * 1. Analyzes URL.
 * 2. Creates controller.
 * 3. Launches action.
 * 4. Renders (if not already rendered).
 */
class Dispatcher
{
	static function dispatch($url)
	{
		# required libs
		require FEATHER.'libs'.DS.'load.php';
		$Load = Load::getInstance();
		$Load->libs('inflector', 'error');
		
		# defaults
		$controllerName = '';
		$action = 'index';
		$params = array(
			'bare'  => false,
			'admin' => false,
		);
		
		# extract params from URL
		$args    = self::parse_and_route_url($url);
		$plugins = self::list_plugins();
		
		# analyze params
		if (!empty($args))
		{
			if ($args[0] == 'bare')
			{
				# bare (useful for AJAX)
				$params['bare'] = true;
				array_shift($args);
			}
			elseif (in_array($args[0], $plugins))
			{
				# plugin!
				$Load->setPlugin($args[0]);
				array_shift($args);
			}
		}
		if (!empty($args))
			$controllerName = array_shift($args);
		if (!empty($args))
			$action = array_shift($args);
		$params['args'] =& $args;
		
		
		# Controller & Action
		
		$Controller = $Load->controller($controllerName, $url, $action, $params);
		$Controller->callback('beforeAction');
		
		if (method_exists($Controller, $action))
			call_user_func_array(array($Controller, $action), $args);
		elseif (!DEBUG)
			Error::show(404);
		else
		{
			trigger_error(sprintf("Controller '%s' has no such action '%s'.",
				$Controller->name, $action), E_USER_ERROR);
		}
		$Controller->callback('afterAction');
		
		
		# View
		if ($Controller->autoRender)
			$Controller->render();
	}
	
	/**
	 * Runs a script.
	 * @return 
	 */
	static function script($path, $action='main', array $args=null)
	{
		# required libs
		require FEATHER.'libs'.DS.'load.php';
		$Load = Load::getInstance();
		$Load->libs('inflector', 'error');
		
		# controller & action
		$Script = $Load->script($path, $action);
		
		if (method_exists($Script, $action))
		{
			$Script->beforeAction();
			if (is_array($args))
			{
				call_user_func_array(array($Script, $action), $args);
			}
			else
			{
				$Script->$action();
			}
			$Script->afterAction();
		}
		else
		{
			trigger_error(sprintf("Script '%s' has no such action '%s'.",
				$Script->name, $action), E_USER_ERROR);
		}
	}
	
	/**
	 * 
	 * @return array Parsed URL
	 * @param $url string
	 */
	static function & parse_and_route_url($url)
	{
		require APP.'config'.DS.'router.php';
		
		# uniforms URL
		$url = preg_replace(':^/:', '', $url);
		if (!preg_match(':/$:', $url))
			$url = "$url/";
		if (isset($url) && $url === 'favicon.ico')
			die();
		
		# routes
		$url = preg_replace(
			array_keys(Router::$routes),
			array_values(Router::$routes),
			$url
		);
		
		# parse url ($controller/$action/$get)
		$args = explode('/', $url);
		foreach ($args as $i => $arg)
		{
			if (empty($arg) and strlen($arg) == 0)
				unset($args[$i]);
		}
		$args = array_values($args);
		
		return $args;
	}
	
	/**
	 * Returns a list of plugins.
	 * 
	 * @return array
	 */
	static function & list_plugins()
	{
		$plugins = cache('list_of_plugins');
		
		if (!empty($plugins) and !DEBUG)
			$plugins = unserialize($plugins);
		else
		{
			$plugins = array();
			if (is_dir(APP.'plugins'))
			{
				if ($dh = opendir(APP.'plugins'))
				{
					while (($fname = readdir($dh)) !== false)
					{
						if (strpos($fname, '.') === false)
							$plugins[] = $fname;
					}
					closedir($dh);
				}
			}
			cache('list_of_plugins', serialize($plugins));
		}
		return $plugins;
	}
}
?>